/******************************************************************************
* echo_server.c                                                               *
*                                                                             *
* Description: This file contains the C source code for an echo server.  The  *
*              server runs on a hard-coded port and simply write back anything*
*              sent to it by connected clients.  It does not support          *
*              concurrent clients.                                            *
*                                                                             *
* Authors: Athula Balachandran <abalacha@cs.cmu.edu>,                         *
*          Wolf Richter <wolf@cs.cmu.edu>                                     *
*                                                                             *
*******************************************************************************/

#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include<parse.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<log.h>
#include<sys/select.h>

#define ECHO_PORT 9999
#define BUF_SIZE 1024 * 80
const char* ROOT_DIR = "./static_site"; //请求文件根目录

/*————————————————打印错误————————————————*/
void Perror(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

/*————————————————设置套接字sockfd为非阻塞————————————————*/
static void setnonblocking(int sockfd)
{
    int flag = fcntl(sockfd, F_GETFL, 0);
    if (flag < 0) 
    {
        Perror("fcntl F_GETFL fail");
        return;
    }
    if (fcntl(sockfd, F_SETFL, flag | O_NONBLOCK) < 0) 
    {
        Perror("fcntl F_SETFL fail");
    }
} 

/*————————————————关闭套接字————————————————*/
int close_socket(int sock)
{
    if (close(sock))
    {
        dump_log("[response] Failed closing socket.\n\n");
        return 1;
    }
    return 0;
}

/*————————————————发送错误400消息————————————————*/
void response400(int client_socket)
{
    dump_log("[response] Bad request\n\n");
    char response[1024];
    strcat(response, "HTTP/1.1 400 Bad request\r\n\r\n");
    send(client_socket, response, strlen(response), 0);
}

/*————————————————发送错误404消息————————————————*/
void response404(int client_socket)
{
    dump_log("[response] request file not found\n\n");
    char response[1024];
    strcat(response, "HTTP/1.1 404 Not Found\r\n\r\n");
    send(client_socket, response, strlen(response), 0);
}

/*————————————————发送错误501消息————————————————*/
void response501(int client_socket)
{
    dump_log("[response] Not Implemented\n\n");
    char response[1024];
    strcat(response, "HTTP/1.1 501 Not Implemented\r\n\r\n");
    send(client_socket, response, strlen(response), 0);\
}
/*————————————————发送错误505消息————————————————*/
void response505(int client_socket)
{
    dump_log("[response] HTTP Version not supported\n\n");
    char response[1024];
    strcat(response, "HTTP/1.1 505 HTTP Version not supported\r\n\r\n");
    send(client_socket, response, strlen(response), 0);
}

/*————————————————获得请求中名称为des的首部行消息————————————————*/
char *getValue(Request *request, const char* des)
{
    /*
    request 是请求
    des 是要查找的首部行的名称
    */
    for(int i = 0; i < request->header_count; i++)
    {
        if(!strcmp(request->headers[i].header_name, des))
        {
            return request->headers[i].header_value;
        }
    }
    dump_log("[response] Lose some message in request\n");
    return NULL;
}

/*————————————————发送路径问url的文件给client_socket————————————————*/
int send_file(int client_socket, char *url)
{
    /*
    client_socket是客户端的套接字
    url是文件路径
    */
    FILE * fd = fopen(url, "rb+"); 
    char msg[BUF_SIZE]; 
    fseek(fd, 0, SEEK_END);
    rewind(fd);
    int index = 0;
    char ch; 
    //将文件内容复制到msg字符数列中
    while((ch = fgetc(fd)) != EOF){
        msg[index++] = ch;
    }
    send(client_socket, msg, strlen(msg), 0);
    dump_log("[INFO] send successful!\n\n");
    return 1;
}

/*————————————————对get方法的请求做出响应————————————————*/ 
void response_get(int client_socket, Request *request)
{
    char response[BUF_SIZE];        //存放协议头部
    memset(response, 0, BUF_SIZE);
	char url[256];                  //存放文件路径
    memset(url, 0, 256);
    struct stat file_s;
    /*————————————————获取文件路径————————————————*/
    strcat(url, ROOT_DIR);
    if(!strcmp(request->http_uri, "/"))                         //默认发送index
        strcat(url, "/index.html");
    else if(strlen(request->http_uri) < 256 - strlen(ROOT_DIR)) //路径长度
        strcat(url, request->http_uri);
    else
    {
        dump_log("[response] request file not found\n");
        response404(client_socket);
        return;
    }

    /*————————————————判断文件存不存在————————————————*/
	if(stat(url, &file_s))                                      
    {
        dump_log("[response] request file not found\n");
        response404(client_socket);
		return;
	}

    /*————————————————判断是否有权限打开————————————————*/
	if(!(S_ISREG(file_s.st_mode)) || !(S_IRUSR & file_s.st_mode))
    {
        dump_log("[response] request file not found\n");
		response404(client_socket);
        return;
	}
    /*
    填充协议头部
    */
    /*————————————————————返回成功代码和信息————————————————————*/
    strcat(response, "HTTP/1.1 200 OK\r\n");

    /*————————————————————服务器名————————————————————*/
    strcat(response, "Server: liso/1.0\r\n");

    /*————————————————————获取当地时间————————————————————*/
    char data[124];
    memset(data, 0, 124);
    strcat(response, "Date: ");
    time_t rt;
    time(&rt);
    strcat(data, ctime(&rt));
    strcat(response, data);

    /*————————————————————获取文件长度————————————————————*/
    struct stat fl;
    stat(url, &fl);
    char length[124];
    memset(length, 0, 124);
    sprintf(length, "%ld", fl.st_size);
    strcat(response, "Content-length: ");
    strcat(response, length);
    strcat(response, "\r\n");

    /*————————————————————获取文件类型————————————————————*/
    if(!strcmp(request->http_uri, "/") || !strcmp(request->http_uri, "/index.html")){
        strcat(response, "Content-type: text/html\r\n");
    }
    else if(!strcmp(request->http_uri, "/images/liso_header.png")){
        strcat(response, "Content-type: image/png\r\n");
    }
    else strcat(response, "Content-type: text/css\r\n");
    /*
    由于只有三个可请求文件，所以直接判断
    */

    /*————————————————————获取文件上次修改时间————————————————————*/
    strcat(response, "Last-modified: ");
    strcat(response, ctime(&fl.st_mtime));

    /*————————————————————连接情况————————————————————*/
    strcat(response, "Connection: close\r\n\r\n");
    
    send(client_socket, response, strlen(response), 0);
    dump_log("have sent head back to client\n");
    send_file(client_socket, url);
}

/*————————————————对head方法的请求做出响应————————————————*/ 
void response_head(int client_socket, Request *request)
{
    char response[BUF_SIZE];        //存放协议头部
    memset(response, 0, BUF_SIZE);
	char url[256];                  //存放文件路径
    memset(url, 0, 256);
    struct stat file_s;
    /*————————————————获取文件路径————————————————*/
    strcat(url, ROOT_DIR);
    if(!strcmp(request->http_uri, "/"))                         //默认发送index
        strcat(url, "/index.html");
    else if(strlen(request->http_uri) < 256 - strlen(ROOT_DIR)) //路径长度
        strcat(url, request->http_uri);
    else
    {
        dump_log("[response] request file not found\n");
        response404(client_socket);
        return;
    }

    /*————————————————判断文件存不存在————————————————*/
	if(stat(url, &file_s))                                      
    {
        dump_log("[response] request file not found\n");
        response404(client_socket);
		return;
	}

    /*————————————————判断是否有权限打开————————————————*/
	if(!(S_ISREG(file_s.st_mode)) || !(S_IRUSR & file_s.st_mode))
    {
        dump_log("[response] request file not found\n");
		response404(client_socket);
        return;
	}
    /*
    填充协议头部
    */
    /*————————————————————返回成功代码和信息————————————————————*/
    strcat(response, "HTTP/1.1 200 OK\r\n");

    /*————————————————————服务器名————————————————————*/
    strcat(response, "Server: liso/1.0\r\n");

    /*————————————————————获取当地时间————————————————————*/
    char data[124];
    memset(data, 0, 124);
    strcat(response, "Date: ");
    time_t rt;
    time(&rt);
    strcat(data, ctime(&rt));
    strcat(response, data);

    /*————————————————————获取文件长度————————————————————*/
    struct stat fl;
    stat(url, &fl);
    char length[124];
    memset(length, 0, 124);
    sprintf(length, "%ld", fl.st_size);
    strcat(response, "Content-length: ");
    strcat(response, length);
    strcat(response, "\r\n");

    /*————————————————————获取文件类型————————————————————*/
    if(!strcmp(request->http_uri, "/") || !strcmp(request->http_uri, "/index.html")){
        strcat(response, "Content-type: text/html\r\n");
    }
    else if(!strcmp(request->http_uri, "/images/liso_header.png")){
        strcat(response, "Content-type: image/png\r\n");
    }
    else strcat(response, "Content-type: text/css\r\n");

    /*————————————————————获取文件上次修改时间————————————————————*/
    strcat(response, "Last-modified: ");
    strcat(response, ctime(&fl.st_mtime));

    /*————————————————————连接情况————————————————————*/
    strcat(response, "Connection: close\r\n\r\n");
    
    send(client_socket, response, strlen(response), 0);
    dump_log("have sent head back to client\n");
}

/*————————————————对post方法的请求做出响应————————————————*/
void response_post(int client_socket, char *buf)
{
    send(client_socket, buf, strlen(buf), 0);
}

/*————————————————处理http请求————————————————*/
void response_http_request(int client_socket, char *buf, int len)
{
    Request *request = parse(buf, len, 0);
    printf("parse结束\n");

    /*————————————————判断请求格式是否正确————————————————*/
    if(request == NULL)
    {
        response400(client_socket);
        return;
    }
    dump_log("parse request successfully!\n");

    /*————————————————判断HTTP版本是否正确————————————————*/
    if(strcmp(request->http_version, "HTTP/1.1"))
    {
        response505(client_socket);
        return;
    }

    /*————————————————判断请求方法————————————————*/
    if(!strcmp(request->http_method, "POST"))
    {
        dump_log("start post\n");
        response_post(client_socket, buf);
    }

    else if(!strcmp(request->http_method, "GET"))
    {
        dump_log("start get\n");
        response_get(client_socket, request);
    }

    else if(!strcmp(request->http_method, "HEAD"))
    {
        dump_log("start head\n");
        response_head(client_socket, request);
    }

    /*————————————————没实现的方法————————————————*/
    else
    {
        response501(client_socket);
    }
}
int main(int argc, char* argv[])
{
    /*————————————————定义常量————————————————*/
    int sock, client_sock, clients[FD_SETSIZE];
    ssize_t readret;
    socklen_t cli_size;
    struct sockaddr_in addr, cli_addr;
    char *buf = (char*)malloc(BUF_SIZE * sizeof(char));
    fd_set rset, rreadyset;

    /*————————————————服务器开启————————————————*/
    init_log();
    fprintf(stdout, "----- Echo Server -----\n");
    dump_log("[Main] ----- Liso Web Server -----\n\n");
    /* all networked programs must create a socket */
    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        dump_log("open_listen error\n\n");
        return EXIT_FAILURE;
    }
    char tmp[1024];
    sprintf(tmp, "[Main] Create listenfd %d sucessfully\n\n", sock);
    dump_log(tmp);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(ECHO_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    /* servers bind sockets to ports---notify the OS they accept connections */
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)))
    {
        close_socket(sock);
        dump_log("[Main] Failed binding socket\n\n");
        return EXIT_FAILURE;
    }


    if (listen(sock, 32))
    {
        close_socket(sock);
        dump_log("[Main] Error listening on socket\n\n");
        return EXIT_FAILURE;
    }

    /* finally, loop waiting for input and then write it back */

    /*————————————————初始化客户池————————————————*/
    int maxfd = sock;
    int nReady = 0;
    FD_ZERO(&rset);
    FD_ZERO(&rreadyset);
    FD_SET(sock,&rreadyset);
    for(int i = 0; i < FD_SETSIZE; i++)
    {
        clients[i] = -1;
    }

    /*————————————————服务器监听套接字循环监听和处理请求————————————————*/
    setnonblocking(sock);
    while (1)
    {       
        rset = rreadyset;
        nReady = select(maxfd + 1, &rset, NULL, NULL, NULL);
        char tmp1[1024];
        sprintf(tmp1, "Select return %d \n\n",nReady);
        dump_log(tmp1);
        if(nReady == 0) //没有客户端请求
        {
            continue;
        }
        else if(nReady < 0) //服务器select发生错误
        {
            return EXIT_FAILURE;
        }
        if(FD_ISSET(sock, &rset))   //有客户端发送请求
        {
            cli_size = sizeof(cli_addr);
            client_sock = accept(sock, (struct sockaddr *) &cli_addr, &cli_size);

            if(client_sock < 0) //连接失败
            {
                dump_log("[Main] Error accepting connection.\n\n");
                continue;
            }

            for(int i = 0; i < FD_SETSIZE; i++) //在客户池找到合适的位置放置客户端
            {
                if(clients[i] < 0)
                {
                    clients[i] = client_sock;
                    FD_SET(client_sock, &rreadyset);
                    char tmp2[1024];
                    sprintf(tmp2, "The clinet socket %d have been put in index %d\n\n", client_sock, i);
                    dump_log(tmp2);
                    maxfd = (client_sock > maxfd) ? client_sock : maxfd;
                    break;
                }
                if(i == FD_SETSIZE - 1) //客户端太多，客户池放不下
                {
                    dump_log("[Client] Too many clients!\n\n");
                }   
            }    
            continue;    
        }
        for(int i = 0; i < FD_SETSIZE; i++) //遍历客户池，处理客户端请求
        {
            if(clients[i] < 0)
            {
                continue;
            }
            client_sock = clients[i];
            if(FD_ISSET(client_sock, &rset))
            {
                /*——————————————从客户端读取信息————————————————*/
                readret = 0;
                readret = recv(client_sock, buf, BUF_SIZE, 0);

                if(readret <= 0)    //读取发生错误
                {
                    if(readret == 0)    
                    {
                        dump_log("[Client] Client is close..\n\n");
                    }
                    else
                    {
                        char t[1024];
                        sprintf(t, "Exception on recv() from client %d on socket\n\n", client_sock);
                        dump_log(t);
                    }   

                    /*————————————————关闭客户端连接，从客户池删除————————————————*/
                    close_socket(client_sock);
                    FD_CLR(client_sock,&rreadyset);
                    clients[i] = -1;
                    continue;
                }
                char tmp3[BUF_SIZE];
                sprintf(tmp3, "Now read client index %d, socket %d\ncontent is %s\n\n", i, client_sock, buf);
                dump_log(tmp3);

                /*————————————————处理客户端的请求————————————————*/
                loc = 0;
                char tmp4[1024];
                sprintf(tmp4, "Now write client index %d, socket %d\n", i, client_sock);
                dump_log(tmp4);
                while(loc < readret - 2)
                {
                    response_http_request(client_sock, buf + loc, readret);
                }   

                /*————————————————处理完客户端请求将其从客户池删除————————————————*/
                memset(buf, 0, BUF_SIZE);
                FD_CLR(client_sock, &rreadyset);
                close_socket(client_sock);
                clients[i] = -1;
            }
        }
    }
    close_socket(sock);
    return EXIT_SUCCESS;
}